<template>
  <el-container style="width: 100%; height: 100%">
    <!-- 头部标题 -->
    <el-header class="header"> GLBLoader</el-header>
    <!-- 主要内容区域，包含 Three.js 画布 -->
    <el-main class="canvas-container">
      <canvas ref="canvas" class="canvas"></canvas>
    </el-main>
  </el-container>
</template>

<script lang="ts">
export default {
  name: 'GLBLoader', // 使用多词名称
}
</script>

<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue'
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import { type GLTF, GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader'
import { MathUtils } from 'three'

// Three.js 相关变量声明
// 场景
let scene: THREE.Scene
// 透视相机
let camera: THREE.PerspectiveCamera
// WebGL渲染器
let renderer: THREE.WebGLRenderer

// 动画帧ID
let animationFrameId: number

let controls: InstanceType<typeof OrbitControls>

let gltfLoader: InstanceType<typeof GLTFLoader>
const mixers: THREE.AnimationMixer[] = [];
// 渲染循环
const clock = new THREE.Clock();

// 画布引用
const canvas = ref<HTMLCanvasElement>()
// 组件挂载时初始化场景并开始动画
onMounted(() => {
  initScene()
  animate()
  // 添加窗口大小改变事件监听
  window.addEventListener('resize', onWindowResize)
})

// 初始化场景
function initScene() {
  if (!canvas.value) return
  makeRenderer(canvas.value.clientWidth, canvas.value.clientHeight)
  makeScene()

  // 加载GLB模型
  gltfLoader = new GLTFLoader()
  const dracoLoader = new DRACOLoader()
  dracoLoader.setDecoderPath('/draco/')
  gltfLoader.setDRACOLoader(dracoLoader)
  gltfLoader.load(
    new URL('@/assets/models/gltf/LittlestTokyo.glb', import.meta.url).href,
    (gltf: GLTF): void => {
      console.log('模型加载完成')
      console.log(gltf)
      // 获取模型对象
      const model = gltf.scene
      model.scale.set(0.05, 0.05, 0.05)
      gltf.animations.forEach((clip) => {
        // 创建动画混合器
        const mixer = new THREE.AnimationMixer(model)
        // 创建动画剪辑
        const action = mixer.clipAction(clip)
        // 播放动画剪辑
        action.play()
        // 将动画混合器存储到数组中
        mixers.push(mixer);

      })
      // 添加模型到场景
      scene.add(model)
    },
    (xhr: ProgressEvent) => {
      console.log((xhr.loaded / xhr.total) * 100 + '% loaded')
    },
    (error: Error) => {
      console.error('An error occurred while loading the model.', error)
    },
  )
  makeCamera(canvas.value.clientWidth, canvas.value.clientHeight)
  orbitControls()
}

function makeScene() {
  // 创建场景
  scene = new THREE.Scene()
  //添加光源
  const light = new THREE.DirectionalLight(0xffffff, 10)
  //添加环境光源
  const ambientLight = new THREE.AmbientLight(0xffffff, 1)
  scene.add(light)
  scene.add(ambientLight)
}
function makeRenderer(width: number, height: number) {
  // 创建WebGL渲染器
  renderer = new THREE.WebGLRenderer({ canvas: canvas.value, antialias: true })
  renderer.setSize(width, height)
  // 设置设备像素比，确保在高分辨率屏幕上清晰显示
  renderer.setPixelRatio(window.devicePixelRatio)
}

function makeCamera(width: number, height: number) {
  // 创建透视相机
  camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000)
  // 设置相机位置
  camera.position.set(0, 10, 20)
  camera.lookAt(0, 0, 0)
}

function orbitControls() {
  controls = new OrbitControls(camera, canvas.value)

  // 限制摄像机的极角范围（单位是弧度）
  controls.minPolarAngle = 0 // 最小角度，0 表示水平视角
  controls.maxPolarAngle = MathUtils.degToRad(85) // 最大角度，例如 85 度

  // 可选：限制摄像机的缩放范围
  controls.minDistance = 5 // 最小距离
  controls.maxDistance = 100 // 最大距离
}

// 动画循环函数
function animate() {
  animationFrameId = requestAnimationFrame(animate)
  controls.update()
  // 计算时间差
  const deltaTime = clock.getDelta();

  // 更新所有动画混合器
  mixers.forEach((mixer) => mixer.update(deltaTime));

  // 渲染场景
  renderer.render(scene, camera)
}

// 清理函数
function cleanup() {
  // 取消动画帧`
  if (animationFrameId) {
    cancelAnimationFrame(animationFrameId)
  }

  // 清理渲染器
  if (renderer) {
    // 移除所有事件监听器
    renderer.domElement.removeEventListener('resize', onWindowResize)
    // 释放渲染器资源
    renderer.dispose()
    // 清空渲染器
    renderer.forceContextLoss()
    // 移除画布
    renderer.domElement.remove()
  }

  // 清理场景
  if (scene) {
    // 遍历场景中的所有对象
    scene.traverse((object) => {
      if (object instanceof THREE.Mesh) {
        // 释放几何体资源
        object.geometry.dispose()
        // 释放材质资源
        if (Array.isArray(object.material)) {
          object.material.forEach((material) => material.dispose())
        } else {
          object.material.dispose()
        }
      }
    })
    // 清空场景
    scene.clear()
  }

  // 清理相机
  if (camera) {
    camera.clear()
  }
}

// 窗口大小改变时的处理函数
function onWindowResize() {
  if (!canvas.value) return
  const width = canvas.value.clientWidth
  const height = canvas.value.clientHeight
  console.log('onWindowResize', width, height)
  // 更新相机
  camera.aspect = width / height
  camera.updateProjectionMatrix()

  // 更新渲染器
  renderer.setSize(width, height)
  // 确保渲染器的像素比与设备匹配
  renderer.setPixelRatio(window.devicePixelRatio)
}

// 组件卸载时清理资源
onUnmounted(() => {
  cleanup()
  // 移除窗口大小改变事件监听
  window.removeEventListener('resize', onWindowResize)
})
</script>

<style scoped>
/* 头部样式 */
.header {
  text-align: center;
  font-size: 20px;
  font-weight: bold;
  color: #333;
}

/* 画布容器样式 */
.canvas-container {
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
}

/* 画布样式   // 保持画布比例*/
.canvas {
  width: 100%;
  height: 100%;
}
</style>
